<?php


/**
* @desc Very Simple Templator - Шаблонизатор VSTpl.
*/
class legotpl
{
    /**
    * @var array Массив переменных, которые будут подставляться в шаблон
    */
    private $vars = array();
    const debug = false; // Принудительная компиляция шаблонов
    
    public $basepath;
    public $common_path;
    public $template_path;
    
    protected $compiledir;
    
    /*************************************************/
    public $compile_time;
    protected $body = "";
    protected $searches = array(
        '@{/foreach}@i',        
        '@{else}@i',        
        '@{/if}@i',        
        '@{/text}@i',
    );
    protected $replaces = array(
        '<?endforeach;?>',
        '<?else:?>',
        '<?endif;?>',
        '<?endif;?>',
    );
    
    function __construct()
    {
        //$this->basepath     = $_SERVER['DOCUMENT_ROOT'] . "/.templates/";
        $this->basepath     = "templates/";
        $this->compiledir = "templates_c/";
        $this->common_basepath = realpath(dirname(__FILE__)."/../../templates/").DIRECTORY_SEPARATOR;
        $this->vars["const"] = array();
        
    }
    
    public function assign($name, $value){
    	$this->vars[$name] = $value;
	}
    
    public function setTemplateDir($template_dir){
    	$this->basepath = $template_dir;
	}
    
    public function setCompileDir($compiledir){
    	$this->compiledir = $compiledir;
	}
    
    /**
    * @desc Обрабатывает шаблон и возвращает результат в переменную
    * @param string $filename Относительный путь к шаблону
    * @returns string Строка содержащая результат обработки шаблона
    */
    public function fetch($filename){
        if(empty($filename)) throw new Exception("Имя шаблона не может быть пустой строкой");   
        // Компилируем
        $compiled_name = $this->compile( $filename );
        extract( $this->vars ); // вытаскиваме переменные в область видимости
        ob_start(); // начинаем перехват вывода
        if(Debug::enabled()) echo "\n<!--begin $filename-->\n";
        include $compiled_name; // инклудим скомпилированный шаблон
        if(Debug::enabled()) echo "\n<!--end $filename-->\n";
        return ob_get_clean();
        
    }
    
    /**
    * @desc Обрабатывает шаблон и выводит результат на экран
    * @param string $filename Относительный путь к шаблону
    */
    public function display($filename){
        echo $this->fetch($filename);
    }
    
    private function getCompiledName($filename){
        $filename = trim( $filename );
        if(!is_dir($this->compiledir)) throw new Exception("Не создана папка для скомпилированных шаблонов {$this->compiledir}");

        // проверяем на валидность имени шаблона
        if(empty($filename)) throw new Exception("Имя шаблона не может быть пустой строкой");
        
        // проверяем на существующий файл
        if ( lego_abstract::current() && file_exists(lego_abstract::current()->getView()->getDir().'/'.$filename) ){
            $this->template_path = lego_abstract::current()->getView()->getDir().'/';
		}
		else
        if ( lego_abstract::current() && file_exists(lego_abstract::current()->getView()->getDefaultDir().'/'.$filename) ){
            $this->template_path = lego_abstract::current()->getView()->getDefaultDir().'/';
		}
		else
        if ( file_exists($this->basepath . $filename) ) 
            $this->template_path = $this->basepath;
        else
        if ( file_exists( $this->common_basepath . $filename) )
            $this->template_path = $this->common_basepath;
        else
        if ( file_exists($filename) ){
            $this->template_path = "";                                    
		}
		else
            throw new Exception("Шаблон '{$filename}' не найден");

        $compiled_name = $this->compiledir . basename($filename).".".md5($this->template_path.$filename).".php";
        return $compiled_name;
	}    
    
    /**
    * @desc Проверяем создаем, компилируем файл
    * @param string $filname Путь к файлу
    * @return string Имя скомпилированного шаблона
    */
    private function compile($filename)
    {
    	$compiled_name = $this->getCompiledName($filename);
        // если шаблон новый, или не существует, или мы выставили принудительное обновление
        if(self::debug || !file_exists( $compiled_name ) || ( filemtime( $this->template_path.$filename ) > filemtime( $compiled_name ) ) )
        {
            $this->body = file_get_contents( $this->template_path.$filename );

            //Замена переменных $var.param.param2.... на $var["param"]["param2"][....
            $compiled = preg_replace( $this->searches, $this->replaces, $this->body );
            $process_tag_fn = array($this,'process_tag');
            
            //Вырезаем комментарии в формате {* ... *}
            $compiled = preg_replace("@{\*.*?\*}@s","",$compiled); 
            
            //Прямой вывод результата функции {= ... =}
            $compiled = preg_replace("@{\=(.*?)\}@s", "<?=\\1?>", $compiled); 
            $compiled = preg_replace_callback("@{.*?}@",$process_tag_fn,$compiled);
            file_put_contents( $compiled_name, $compiled );
        }
        return $compiled_name;
    }
    
    /**
    * @desc Конвертирует переменные вида $var.param.param2 в $var["param"]["param2"]
    */
    private function vars_replace($matches)
    {                                 
        $result = preg_replace('@\.([a-z0-9_]+)@i','["$1"]', $matches );
        return preg_replace('@\->([a-z0-9_]+)@i','->$1', $result );
    }

    
    protected function process_tag($match)
    {
        $result = $match[0]; //По умолчанию считаем что тег не стоит трогать
    
        if (preg_match("@{([a-z]+?) (.+?)}@i", $match[0], $tag)) //Сложный тег
        {
            //Находим все переметры с постоянными величинами (в кавычках)
            preg_match_all('@([a-z]+?)="([^"]+)"@i', $tag[2], $params_const);
            //Находим все параметры с переменными величинами ($var)
            preg_match_all('@([a-z]+?)=(\\$[^ ]+)@i', $tag[2], $params_var);
            //Преобразуем $arr.param в $arr["param"]
            $params_var[2] = $this->vars_replace($params_var[2]);
            //И объединили массивы назад
            $keys = array_merge($params_var[1], $params_const[1]);
            $values = array_merge($params_var[2], $params_const[2]);
            $params = (!empty($keys)) ? array_combine($keys,$values) : array();
            //Если параметры вида aaa="bbb" не найдено, значит там один единственный парам
            if(empty($params)) $params[] = $tag[2];
            //Вызываем функцию из папки plugins по имени тега
            if (file_exists(dirname(__FILE__).'/plugins/tag_'.$tag[1].'.php')){
            	require_once(dirname(__FILE__).'/plugins/tag_'.$tag[1].'.php');
            	$function_name = 'VSTpl\tag_' . $tag[1];
            	$result = $function_name($params);
			}
        }
        else if (preg_match("@{([a-z]+?)}@i",$match[0],$tag)) //Простой тег
        {
            if (method_exists($this,"tag_" . $tag[1])) $result = $this->{"tag_" . $tag[1]}(null);
        }
        else if (preg_match('@{(\\$.+?)}@i',$match[0],$var)) //echo
        {                                                
            $result = '<?=' . $this->vars_replace($var[1]) . "?>";
        }
        return $result;
    } 
}    

